﻿/*
 * sdc_csp.c - SDC host driver code for LomboTech
 * sdc hardware operation
 *
 * Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#ifndef ___SDC___CSP__C___
#define ___SDC___CSP__C___

#include <mach/csp.h>
#include "sdc_const.h"
#include "sdc_csp.h"

/* #define ENABLE_TRACE */
#include <mach/debug.h>
#undef MOD_NAME
#define MOD_NAME	"SDC"

/******************************************************************************
 * sdc clock control
 *****************************************************************************/

/* Make the clk register take effect at once */
void csp_sdc_update_clk_pre(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_cmd_t cmd_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px\n", base_addr);

	/* UCF pending indicate clock update finished,
	 * clear all pending before clock update. */
	WRITEREG32(&sdc->int_clr, SDC_INT_ALL_MASK);
	cmd_mask.val = READREG32(&sdc->cmd);
	/* Info CIU only to update clk_src, clk_en, clk_div register */
	cmd_mask.bits.update_clk_reg_only = 1;
	WRITEREG32(&sdc->cmd, cmd_mask.val);

	/* start to update */
	cmd_mask.bits.start_cmd = 1;
	WRITEREG32(&sdc->cmd, cmd_mask.val);

	PRT_TRACE_END("\n");
}

/* Restore the cmd register after clock updated. */
/* We should called this after we have got the UCF pending. */
void csp_sdc_update_clk_post(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_cmd_t cmd_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px\n", base_addr);

	cmd_mask.val = READREG32(&sdc->cmd);

	/* return back normal mode */
	cmd_mask.bits.update_clk_reg_only = 0;
	WRITEREG32(&sdc->cmd, cmd_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_set_clk_div - set the internal clock divder
 * @base_addr:	base address for SDC
 * @val:	clock divder value, we always set it 0 to bypass the divder
 */
void csp_sdc_set_clk_div(void *base_addr, u32 val)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;

	PRT_TRACE_BEGIN("base_addr=0x%px,val=%d\n", base_addr, val);

	WRITEREG32(&sdc->clk_div, val);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_set_clk_src - set the internal clock source
 * @base_addr:	base address for SDC
 * @val:	clock source index, always 0
 */
void csp_sdc_set_clk_src(void *base_addr, u32 val)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;

	PRT_TRACE_BEGIN("base_addr=0x%px,val=%d\n", base_addr, val);

	WRITEREG32(&sdc->clk_src, val);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_enable_clk - enable/disable the SDC clk
 * @base_addr:	base address for SDC
 * @enable:	0, disable; 1, enable
 */
void csp_sdc_enable_clk(void *base_addr, u32 enable)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_clk_en_t clk_en_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px,enable=%d\n", base_addr, enable);

	clk_en_mask.val = READREG32(&sdc->clk_en);
	clk_en_mask.bits.clk_en0 = enable;
	WRITEREG32(&sdc->clk_en, clk_en_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_enable_clk_lp - enable/disable the SDC clk low power mode
 * @base_addr:	base address for SDC
 * @enable:	0, disable; 1, enable
 */
void csp_sdc_enable_clk_lp(void *base_addr, u32 enable)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_clk_en_t clk_en_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px,enable=%d\n", base_addr, enable);

	clk_en_mask.val = READREG32(&sdc->clk_en);
	clk_en_mask.bits.clk_lp0 = enable;
	WRITEREG32(&sdc->clk_en, clk_en_mask.val);

	PRT_TRACE_END("\n");
}

/* config the SDC clk */
void csp_sdc_set_clk(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_clk_en_t clk_en_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px\n", base_addr);

#if 1
	/* set internal clock src 0 (default) */
	WRITEREG32(&sdc->clk_src, 0);
	/* bypass internal clock divder (default) */
	WRITEREG32(&sdc->clk_div, 0);
#endif

	clk_en_mask.val = READREG32(&sdc->clk_en);
	/* enable low power mode */
	clk_en_mask.bits.clk_lp0 = 1;
	/* enable the SDC clock */
	clk_en_mask.bits.clk_en0 = 1;
	WRITEREG32(&sdc->clk_en, clk_en_mask.val);

	PRT_TRACE_END("\n");
}

/******************************************************************************
 * sdc reset control
 *****************************************************************************/

/* reset the IDMA interface and FIFO */
void csp_sdc_data_reset(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_ctrl_t ctrl_mask;
	reg_sdc_idma_bus_mod_t idma_bus_mod_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px\n", base_addr);

	ctrl_mask.val = READREG32(&sdc->ctrl);
	ctrl_mask.bits.fifo_reset = 1;
	ctrl_mask.bits.idma_reset = 1;
	WRITEREG32(&sdc->ctrl, ctrl_mask.val);

#if 1
	/* reset the IDMA register */
	idma_bus_mod_mask.val = READREG32(&sdc->idma_bus_mod);
	idma_bus_mod_mask.bits.dswr = 1;
	WRITEREG32(&sdc->idma_bus_mod, idma_bus_mod_mask.val);
#endif

	PRT_TRACE_END("\n");
}

#if 0
/* reset the IDMA register */
void csp_sdc_idma_soft_reset(void *base_addr)
{
	reg_sdc_idma_bus_mod_t idma_bus_mod_mask;

	PRT_TRACE_BEGIN("base_addr=%x\n", base_addr);

	idma_bus_mod_mask.val = READREG32(&sdc->idma_bus_mod);
	idma_bus_mod_mask.bits.dswr = 1;
	WRITEREG32(&sdc->idma_bus_mod, idma_bus_mod_mask.val);

	PRT_TRACE_END("\n");
}
#endif

/* reset the sdc controller
 * Note: does not affect any registers or DMA interface */
void csp_sdc_host_reset(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_ctrl_t ctrl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px\n", base_addr);

	ctrl_mask.val = READREG32(&sdc->ctrl);
	ctrl_mask.bits.sdc_reset = 1;
	WRITEREG32(&sdc->ctrl, ctrl_mask.val);

	PRT_TRACE_END("\n");
}

/* reset the whole SDC */
void csp_sdc_reset_all(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_ctrl_t ctrl_mask;
	reg_sdc_idma_bus_mod_t idma_bus_mod_mask;

	PRT_TRACE_BEGIN("base_addr=%x\n", base_addr);

	ctrl_mask.val = READREG32(&sdc->ctrl);
	/* reset the FIFO */
	ctrl_mask.bits.fifo_reset = 1;
	WRITEREG32(&sdc->ctrl, ctrl_mask.val);

	/* reset the idma register at last */
	idma_bus_mod_mask.val = READREG32(&sdc->idma_bus_mod);
	/* disable the IDMAC */
	idma_bus_mod_mask.bits.de = 0;
	WRITEREG32(&sdc->idma_bus_mod, idma_bus_mod_mask.val);

	ctrl_mask.bits.use_idmac = 0;
	WRITEREG32(&sdc->ctrl, ctrl_mask.val);

	/* reset the IDMA interface */
	ctrl_mask.bits.idma_reset = 1;
	WRITEREG32(&sdc->ctrl, ctrl_mask.val);

	idma_bus_mod_mask.bits.dswr = 1;
	WRITEREG32(&sdc->idma_bus_mod, idma_bus_mod_mask.val);

	/* sdc controller is lay on the FIFO an IDMA interface,
	 * so reset afterward. */
	ctrl_mask.bits.sdc_reset = 1;
	WRITEREG32(&sdc->ctrl, ctrl_mask.val);

	PRT_TRACE_END("\n");
}

/******************************************************************************
 * sdc voltage control
 *****************************************************************************/

/**
 * csp_sdc_set_voltage - Info the signal voltage level to the SDC
 * @base_addr:	base address for SDC
 * @volt:	voltage level bit, can be SDC_VOLT_3_3 or SDC_VOLT_1_8
 */
void csp_sdc_set_voltage(void *base_addr, u32 volt)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_ctrl_t ctrl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px,volt=%d\n", base_addr, volt);

	ctrl_mask.val = READREG32(&sdc->ctrl);
	ctrl_mask.bits.volt_reg0 = volt;
	WRITEREG32(&sdc->ctrl, ctrl_mask.val);

	PRT_TRACE_END("\n");
}

/******************************************************************************
 * sdc calibrate control
 *****************************************************************************/

/* start calibrate */
void csp_sdc_clbr_start(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_calib_t calib_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px\n", base_addr);

	calib_mask.val = READREG32(&sdc->calib);
	calib_mask.bits.cclk_ca_en = 1;
	WRITEREG32(&sdc->calib, calib_mask.val);

	PRT_TRACE_END("\n");
}

/* check whether calibrate done */
u32 csp_sdc_is_clbr_done(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_calib_t calib_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px\n", base_addr);

	calib_mask.val = READREG32(&sdc->calib);

	PRT_TRACE_END("cclk_ca_done=%d\n", calib_mask.bits.cclk_ca_done);

	return calib_mask.bits.cclk_ca_done;
}

/* check whether calibrate locked */
u32 csp_sdc_is_clbr_locked(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_calib_t calib_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px\n", base_addr);

	calib_mask.val = READREG32(&sdc->calib);

	PRT_TRACE_END("cclk_ca_status=%d\n",
			calib_mask.bits.cclk_ca_locked);

	return calib_mask.bits.cclk_ca_locked;
}

/* get calibrate result */
u32 csp_sdc_get_clbr_status(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_calib_t calib_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px\n", base_addr);

	calib_mask.val = READREG32(&sdc->calib);

	PRT_TRACE_END("cclk_ca_status=%d\n",
			calib_mask.bits.cclk_ca_status);

	return calib_mask.bits.cclk_ca_status;
}

/******************************************************************************
 * sdc timing control
 *****************************************************************************/

/**
 * csp_sdc_set_timing - Set the signal timing
 * @base_addr:	base address for SDC
 * @tx_phase:	TX phase (can be SDC_TX_PHASE_90、180)
 * @rx_phase:	RX phase (can be SDC_RX_PHASE_0、90、180、270)
 * @rx_cclk_dly:	delay between cclk_in_sample and cclk_in
 */
void csp_sdc_set_timing(void *base_addr, u32 tx_phase, u32 rx_phase,
			u32 rx_cclk_dly)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_timing_t timing_mask;

	PRT_TRACE_BEGIN("base_addr=%x,tx_phase=%d,rx_phase=%d,rx_cclk_dly=%d\n",
		  base_addr, tx_phase, rx_phase, rx_cclk_dly);

	timing_mask.val = READREG32(&sdc->timing);

	timing_mask.bits.rx_cdly_clk_en = 0;
	WRITEREG32(&sdc->timing, timing_mask.val);

	timing_mask.bits.rx_phase = rx_phase;
	timing_mask.bits.rx_cclk_dly = rx_cclk_dly;
	timing_mask.bits.tx_phase = tx_phase;
	WRITEREG32(&sdc->timing, timing_mask.val);

	timing_mask.bits.rx_cdly_clk_en = 1;
	WRITEREG32(&sdc->timing, timing_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_set_timing - Set the signal timing
 * @base_addr:	base address for SDC
 * @rx_cclk_dly:	delay between cclk_in_sample and cclk_in
 */
void csp_sdc_set_rx_dly(void *base_addr, u32 rx_cclk_dly)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_timing_t timing_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px,rx_cclk_dly=%d\n",
			base_addr, rx_cclk_dly);

	timing_mask.val = READREG32(&sdc->timing);

	timing_mask.bits.rx_cdly_clk_en = 0;
	WRITEREG32(&sdc->timing, timing_mask.val);

	timing_mask.bits.rx_cclk_dly = rx_cclk_dly;
	WRITEREG32(&sdc->timing, timing_mask.val);

	timing_mask.bits.rx_cdly_clk_en = 1;
	WRITEREG32(&sdc->timing, timing_mask.val);

	PRT_TRACE_END("\n");
}

/******************************************************************************
 * sdc timeout control
 *****************************************************************************/

/**
 * csp_sdc_set_rsp_timeout - Set the response timeout clock count
 * @base_addr:	base address for SDC
 * @rto:	response timeout clock count
 */
void csp_sdc_set_rsp_timeout(void *base_addr, u32 rto)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_to_t to_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px,rto=%d\n", base_addr, rto);

	to_mask.val = READREG32(&sdc->to);
	to_mask.bits.rsp_to = rto;
	WRITEREG32(&sdc->to, to_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_set_data_timeout - Set the response timeout clock count
 * @base_addr:	base address for SDC
 * @long_step:	0, means step is 1; 1, means step is SDC_LONG_TO_STEP_CNT
 * @dto_cnt:	data timeout count
 */
void csp_sdc_set_data_timeout(void *base_addr, u32 long_step, u32 dto_cnt)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_to_t to_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px,long_step=%d,dto_cnt=%d\n",
		  base_addr, long_step, dto_cnt);

	to_mask.val = READREG32(&sdc->to);
	to_mask.bits.data_to_step = long_step;
	to_mask.bits.data_to_cnt = dto_cnt;
	WRITEREG32(&sdc->to, to_mask.val);

	PRT_TRACE_END("\n");
}

/******************************************************************************
 * sdc bus width control
 *****************************************************************************/

/**
 * csp_sdc_set_bus_width - Set the host bus width
 * @base_addr:	base address for SDC
 * @width:	bus width indicator (can be SDC_BUS_WIDTH_1、4、8)
 */
void csp_sdc_set_bus_width(void *base_addr, u32 width)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_cbus_width_t cbus_width_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px,width=%d\n", base_addr, width);

	cbus_width_mask.val = READREG32(&sdc->cbus_width);
	cbus_width_mask.bits.card0_width = width;
	WRITEREG32(&sdc->cbus_width, cbus_width_mask.val);

	PRT_TRACE_END("\n");
}

/******************************************************************************
 * sdc data transfer configure
 *****************************************************************************/

/**
 * csp_sdc_set_fifo_th - Set fifo threshold for RX and TX
 * @base_addr:	base address for SDC
 * @rx_wm:	rx water mark
 * @tx_wm:	tx water mark
 */
void csp_sdc_set_fifo_th(void *base_addr, u32 rx_wm, u32 tx_wm)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_fifo_th_t fifo_th_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px,rx_wm=%d,tx_wm=%d\n",
		base_addr, rx_wm, tx_wm);

	fifo_th_mask.val = READREG32(&sdc->fifo_th);
	fifo_th_mask.bits.rx_wm = rx_wm;
	fifo_th_mask.bits.tx_wm = tx_wm;
	WRITEREG32(&sdc->fifo_th, fifo_th_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_set_fifo_th - Set fifo threshold for RX and TX
 * @base_addr:	base address for SDC
 * @burst_len:	IDMA burst len (can be SDC_IDMA_BURST_1、4、8、16)
 */
void csp_sdc_set_idma_burst(void *base_addr, u32 burst_len)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_fifo_th_t fifo_th_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px,burst_len=%d\n", base_addr, burst_len);

	fifo_th_mask.val = READREG32(&sdc->fifo_th);
	fifo_th_mask.bits.dma_multi_size = burst_len;
	WRITEREG32(&sdc->fifo_th, fifo_th_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_set_card_th - Set card threshold for read and write
 * @base_addr:	base address for SDC
 * @cth:	card threshold
 * @cwte:	enable card write threshold or not; 0, disable; 1, enable
 * @crte:	enable card read threshold or not; 0, disable; 1, enable
 */
void csp_sdc_set_card_th(void *base_addr, u32 cth, u32 cwte, u32 crte)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_card_th_t card_th_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px,cth=%d,cwte=%d,crte=%d\n",
		base_addr, cth, cwte, crte);

	card_th_mask.val = 0;
	card_th_mask.bits.cth = cth;
	card_th_mask.bits.cwte = cwte;
	card_th_mask.bits.crte = crte;
	WRITEREG32(&sdc->card_th, card_th_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_set_trans_mode - Set card threshold for read and write
 * @base_addr:	base address for SDC
 * @trans_mode:	transfer mode (SDC_TRANS_MODE_POLL or SDC_TRANS_MODE_IDMA)
 */
void csp_sdc_set_trans_mode(void *base_addr, u32 trans_mode)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_ctrl_t ctrl_mask;
	reg_sdc_idma_bus_mod_t idma_bus_mod_mask;

	PRT_TRACE_BEGIN("base_addr=%px,trans_mode=%d\n", base_addr, trans_mode);

	ctrl_mask.val = READREG32(&sdc->ctrl);
	idma_bus_mod_mask.val = READREG32(&sdc->idma_bus_mod);

	if (trans_mode == SDC_TRANS_MODE_IDMA) {
		/* Info the host to use IDMAC. */
		ctrl_mask.bits.use_idmac = 1;
		ctrl_mask.bits.idma_reset = 1;
		/* enable the IDMAC */
		idma_bus_mod_mask.bits.de = 1;
		idma_bus_mod_mask.bits.fb = 0;
	} else {
		/* Info the host not to use IDMAC. */
		ctrl_mask.bits.use_idmac = 0;
		/* In polling mode, must not enable the IDMAC,
		 * it may cause the IDMA state-machine run error. */
		idma_bus_mod_mask.bits.de = 0;
	}

	WRITEREG32(&sdc->ctrl, ctrl_mask.val);
	WRITEREG32(&sdc->idma_bus_mod, idma_bus_mod_mask.val);

	if (trans_mode == SDC_TRANS_MODE_IDMA) {
		reg_sdc_idma_int_en_t idma_int_en_mask;

		/* idma-int-pending can be detect only after the
		 * correspond interrput is enabled. */
		idma_int_en_mask.val = READREG32(&sdc->idma_int_en);

		/* enable transmit interrput */
		idma_int_en_mask.bits.rie = 1;
		/* enable receive interrput */
		idma_int_en_mask.bits.tie = 1;

		WRITEREG32(&sdc->idma_int_en, idma_int_en_mask.val);
	}

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_set_byte_cnt - Info SDC how many bytes to transfer
 * @base_addr:	base address for SDC
 * @byte_cnt:	bytes to transfer
 */
void csp_sdc_set_byte_cnt(void *base_addr, u32 byte_cnt)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;

	PRT_TRACE_BEGIN("base_addr=0x%px,byte_cnt=%d\n", base_addr, byte_cnt);

	WRITEREG32(&sdc->byte_cnt, byte_cnt);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_set_byte_cnt - Info SDC the data block size
 * @base_addr:	base address for SDC
 * @blk_size:	data block size
 */
void csp_sdc_set_blk_size(void *base_addr, u32 blk_size)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;

	PRT_TRACE_BEGIN("base_addr=0x%px,blk_size=%d\n", base_addr, blk_size);

	WRITEREG32(&sdc->block_size, blk_size);

	PRT_TRACE_END("\n");
}

/******************************************************************************
 * sdc command configure
 *****************************************************************************/

/**
 * csp_sdc_set_init_seq - Set clocks count for init seq
 * @base_addr:	base address for SDC
 * @seq_cnt:	clocks count for init seq, should be multiple of 8,
 *		upper limit is 128, typically 8 or 80.
 */
void csp_sdc_set_init_seq(void *base_addr, u32 seq_cnt)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_ctrl_t ctrl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px,seq_cnt=%d\n", base_addr, seq_cnt);

	ctrl_mask.val = READREG32(&sdc->ctrl);
	ctrl_mask.bits.init_seq_cnt = ((seq_cnt / 8) - 1);

	WRITEREG32(&sdc->ctrl, ctrl_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_send_cmd - Set clocks count for init seq
 * @base_addr:	base address for SDC
 * @cmd_index:	command index
 * @arg:	command argument
 * @conf:	command configure
 *
 * return 0 when success, otherwise error line number
 */
u32 csp_sdc_send_cmd(void *base_addr, u32 cmd_index, u32 arg, u32 conf)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_cmd_t cmd_mask;

	if ((base_addr == 0) || (cmd_index > SD_MAX_CMD_INDEX)) {
		PRT_ERR("base_addr=0x%px,cmd_index=%d,arg=%x,conf=%x\n",
			base_addr, cmd_index, arg, conf);
		return __LINE__;
	}

	PRT_TRACE_BEGIN("base_addr=0x%px,cmd_index=%d,arg=%x,conf=%x\n",
		  base_addr, cmd_index, arg, conf);

	/*
	 * clear all int/idma-int pending
	 */

	/* clear all int pending */
	/* WRITEREG32(&sdc->int_clr, SDC_INT_ALL_MASK); */
	/* clear all idma-int pending */
	WRITEREG32(&sdc->idma_int_sta, SDC_INT_ALL_MASK);

	/*
	 * set command argument register
	 */
	WRITEREG32(&sdc->cmd_arg, arg);

	/*
	 * set command control register
	 */
	/* only change the bit we need, some bit should not be changed */
	cmd_mask.val = READREG32(&sdc->cmd);
	cmd_mask.val = (cmd_mask.val & (~(SDC_CMD_MASK))) | conf;

	/* set cmd index */
	cmd_mask.bits.cmd_index	= cmd_index;
	WRITEREG32(&sdc->cmd, cmd_mask.val);

	/* now all configure is ok, start the cmd */
	cmd_mask.bits.start_cmd = 1;
	WRITEREG32(&sdc->cmd, cmd_mask.val);

	PRT_TRACE_END("\n");
	return 0;
}

/******************************************************************************
 * sdc interrput enable/pending/clear
 *****************************************************************************/

/**
 * csp_sdc_int_enable - Global interrupt enable/disable
 * @base_addr:	base address for SDC
 * @enable:	0: disable, 1: enable
 */
void csp_sdc_int_enable(void *base_addr, u32 enable)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_ctrl_t ctrl_mask;

	PRT_TRACE_BEGIN("base_addr=0x%px,enable=%d\n", base_addr, enable);

	ctrl_mask.val = READREG32(&sdc->ctrl);
	ctrl_mask.bits.int_en = enable;
	WRITEREG32(&sdc->ctrl, ctrl_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_clear_set_ints - clear some int enable bit,
 *			    and than set other int enable bit
 * @base_addr:	base address for SDC
 * @clear_mask:	int enable bits to be clear
 * @set_mask:	int enable bits to be set
 */
void csp_sdc_clear_set_ints(void *base_addr, u32 clear_mask, u32 set_mask)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	u32 int_en;

	PRT_TRACE_BEGIN("base_addr=0x%px,clear_mask=%x,set_mask=%x\n",
		base_addr, clear_mask, set_mask);

	int_en = READREG32(&sdc->int_en);
	int_en &= ~clear_mask;
	int_en |= set_mask;
	WRITEREG32(&sdc->int_en, int_en);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_enable_ints - enable the specified interrputs
 * @base_addr:	base address for SDC
 * @int_mask:	int enable bits to be set
 */
void csp_sdc_enable_ints(void *base_addr, u32 int_mask)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	u32 int_en;

	PRT_TRACE_BEGIN("base_addr=0x%px,int_mask=%x\n", base_addr, int_mask);

	int_en = READREG32(&sdc->int_en);
	int_en |= int_mask;
	WRITEREG32(&sdc->int_en, int_en);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_disable_ints - enable the specified interrputs
 * @base_addr:	base address for SDC
 * @int_mask:	int enable bits to be set
 */
void csp_sdc_disable_ints(void *base_addr, u32 int_mask)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	u32 int_en;

	PRT_TRACE_BEGIN("base_addr=0x%px,int_mask=%x\n", base_addr, int_mask);

	int_en = READREG32(&sdc->int_en);
	int_en &= ~int_mask;
	WRITEREG32(&sdc->int_en, int_en);

	PRT_TRACE_END("\n");
}

/* get int pending status */
u32 csp_sdc_get_int_pd(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;

	return READREG32(&sdc->int_pd);
}

/**
 * csp_sdc_clr_pd - clear int pending status
 * @base_addr:	base address for SDC
 * @pd_mask:	int pending bits status to be clear
 */
void csp_sdc_clr_pd(void *base_addr, u32 pd_mask)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;

	PRT_TRACE_BEGIN("base_addr=0x%px,pd_mask=%x\n", base_addr, pd_mask);

	WRITEREG32(&sdc->int_clr, pd_mask);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_chk_err - check whether there are error pendings occur
 * @base_addr:	base address for SDC
 *
 * return 0 when not error, otherwise 1
 */
u32 csp_sdc_chk_err(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	u32 int_pd_tmp = 0;
	u32 ret = 0;

	PRT_TRACE_BEGIN("base_addr=0x%px\n", base_addr);

	int_pd_tmp = READREG32(&sdc->int_pd);
	/* report error when any error pending bit occur */
	if ((int_pd_tmp & SDC_INT_ALL_ERR) != 0) {
		PRT_ERR("int_pd=0x%08x\n", int_pd_tmp);
		ret = 1;
	}

	PRT_TRACE_END("int_pd=%x\n", int_pd_tmp);
	return ret;
}

/**
 * csp_sdc_chk_pd - check whether all the expected int pending have been raise
 * @base_addr:	base address for SDC
 * @pd_mask:	int pending bits to be check
 *
 * return 1 when all the expected pending bits are set, otherwise 0
 */
u32 csp_sdc_chk_pd(void *base_addr, u32 pd_mask)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	u32 int_pd_tmp;
	u32 ret = 0;

	PRT_TRACE_BEGIN("base_addr=0x%px,pd_mask=%x\n", base_addr, pd_mask);

	int_pd_tmp = READREG32(&sdc->int_pd);

	/* return 1 only when all bits in pd_mask are set */
	if ((int_pd_tmp & pd_mask) == pd_mask)
		ret = 1;

	PRT_TRACE_END("int_pd=%x\n", int_pd_tmp);
	return ret;
}

/******************************************************************************
 * sdc status
 *****************************************************************************/

/* get the raw status of SDC */
u32 csp_sdc_get_status(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;

	return READREG32((unsigned long)(&sdc->status));
}

/**
 * csp_sdc_chk_rsp_index - check whether the command index is right
 * @base_addr:	base address for SDC
 * @cmd_index:	expected command index
 *
 * return 0 when right, otherwise 1
 */
u32 csp_sdc_chk_rsp_index(void *base_addr, u32 cmd_index)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_status_t status_mask;

	status_mask.val = READREG32(&sdc->status);
	return (status_mask.bits.rsp_index != cmd_index);
}

/******************************************************************************
 * sdc response
 *****************************************************************************/

/* get the raw response from rsp0 register */
u32 csp_sdc_get_rsp0(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;

	return READREG32((unsigned long)(&sdc->rsp0));
}

/* get the raw response from rsp1 register */
u32 csp_sdc_get_rsp1(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;

	return READREG32((unsigned long)(&sdc->rsp1));
}

/* get the raw response from rsp2 register */
u32 csp_sdc_get_rsp2(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;

	return READREG32((unsigned long)(&sdc->rsp2));
}

/* get the raw response from rsp3 register */
u32 csp_sdc_get_rsp3(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;

	return READREG32((unsigned long)(&sdc->rsp3));
}

/******************************************************************************
 * sdc FIFO status
 *****************************************************************************/

/* get data count in FIFO */
u32 csp_sdc_get_fifo_cnt(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	reg_sdc_status_t status_mask;

	status_mask.val = READREG32(&sdc->status);
	return status_mask.bits.fifo_cnt;
}

/******************************************************************************
 * sdc polling read/write
 *****************************************************************************/

/**
 * csp_sdc_pio_write - polling write (CPU mode write)
 * @base_addr:	base address for SDC
 * @buf:	point to the data buffer
 * @byte_cnt:	bytes to write (should be multiple of 4)
 */
u32 csp_sdc_pio_write(void *base_addr, void *buf, u32 byte_cnt)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	u32 *pdata = (u32 *)buf;
	u32 byte_done = 0;

	if ((base_addr == 0) || (buf == NULL) ||
	    (byte_cnt == 0) || ((byte_cnt % 4) != 0)) {
		PRT_ERR("base_addr=0x%px,buf=%p,byte_cnt=%d\n",
			base_addr, buf, byte_cnt);
		return __LINE__;
	}

	PRT_TRACE_BEGIN("base_addr=0x%px,buf=0x%px,byte_cnt=%d\n",
			base_addr, buf, byte_cnt);

	while (byte_done < byte_cnt) {
		WRITEREG32(&sdc->data, *pdata);
		pdata++;
		byte_done += 4;
	}

	PRT_TRACE_END("byte_done=%x\n", byte_done);
	return 0;
}

/**
 * csp_sdc_pio_read - polling read (CPU mode read)
 * @base_addr:	base address for SDC
 * @buf:	point to the data buffer
 * @byte_cnt:	bytes to read (should be multiple of 4)
 */
u32 csp_sdc_pio_read(void *base_addr, void *buf, u32 byte_cnt)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	u32 *pdata = (u32 *)buf;
	u32 byte_done = 0;

	if ((base_addr == 0) || (buf == NULL) ||
	    (byte_cnt == 0) || ((byte_cnt % 4) != 0)) {
		PRT_ERR("base_addr=0x%px,buf=0x%px,byte_cnt=%d\n",
			base_addr, buf, byte_cnt);
		return __LINE__;
	}

	PRT_TRACE_BEGIN("base_addr=0x%p,buf=0x%p,byte_cnt=%d\n",
			base_addr, buf, byte_cnt);

	while (byte_done < byte_cnt) {
		*pdata = READREG32(&sdc->data);
		pdata++;
		byte_done += 4;
	}

	PRT_TRACE_END("byte_done=%x\n", byte_done);
	return 0;
}

/******************************************************************************
 * sdc IDMA
 *****************************************************************************/

/**
 * csp_sdc_idma_desc_conf - configure the idma descriptors table
 * @base_addr:	the first descriptor's start address
 * @skip_len:	gap between two descriptors (only make sense in dual-buf mode )
 * @ram_sle:	select descriptors location
 *		(can be SDC_IDMA_DESC_IN_SRAM or SDC_IDMA_DESC_IN_DDR)
 */
void csp_sdc_idma_desc_conf(void *base_addr, u32 desc_ba, u32 skip_len,
			    u32 ram_sle)
{
	reg_sdc_idma_bus_mod_t idma_bus_mod_mask;
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;

	PRT_TRACE_BEGIN("base_addr=0x%px,desc_ba=%x,skip_len=%x,ram_sle=%d\n",
			base_addr, desc_ba, skip_len, ram_sle);

	/* set descriptor's start address */
	WRITEREG32(&sdc->idma_des_base_addr0, desc_ba);

	/* set skip len and descriptors loction */
	idma_bus_mod_mask.val = READREG32(&sdc->idma_bus_mod);
	idma_bus_mod_mask.bits.dsl = skip_len;
	idma_bus_mod_mask.bits.ram_sle = ram_sle;
	WRITEREG32(&sdc->idma_bus_mod, idma_bus_mod_mask.val);

	PRT_TRACE_END("\n");
}

/**
 * csp_sdc_chk_idma_err - configure the idma descriptors table
 * @base_addr:	the first descriptor's start address
 * @skip_len:	gap between two descriptors (only make sense in dual-buf mode )
 * @ram_sle:	select descriptors location
 *		(can be SDC_IDMA_DESC_IN_SRAM or SDC_IDMA_DESC_IN_DDR)
 */
u32 csp_sdc_chk_idma_err(void *base_addr)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	u32 int_pd_tmp = 0;
	u32 ret = 0;

	PRT_TRACE_BEGIN("base_addr=0x%px\n", base_addr);

	int_pd_tmp = READREG32(&sdc->idma_int_sta);

	/* report error when any error pending bit occur */
	if ((int_pd_tmp & SDC_IDMA_INT_ALL_ERR) != 0) {
		PRT_ERR("idma_int_pd=0x%08x\n", int_pd_tmp);
		ret = 1;
	}

	PRT_TRACE_END("int_pd=%x\n", int_pd_tmp);
	return ret;
}

/**
 * csp_sdc_chk_idma_pd - check whether all the expected idma-int pending have been raise
 * @base_addr:	base address for SDC
 * @pd_mask:	idma-int pending bits to be check
 *
 * return 1 when all the expected pending bits are set, otherwise 0
 */
u32 csp_sdc_chk_idma_pd(void *base_addr, u32 pd_mask)
{
	reg_sdc_t *sdc = (reg_sdc_t *)base_addr;
	u32 int_pd_tmp = 0;
	u32 ret = 0;

	PRT_TRACE_BEGIN("base_addr=0x%px,pd_mask=%x\n", base_addr, pd_mask);

	int_pd_tmp = READREG32(&sdc->idma_int_sta);

	if ((int_pd_tmp & pd_mask) == pd_mask)
		ret = 1;

	PRT_TRACE_END("int_pd=%x\n", int_pd_tmp);
	return ret;
}

/* dump SDC registers for debug */
void csp_sdc_dumpregs(void *base_addr, const char *host_name)
{
	u32 offset = 0;

	PRT_DBG(" =========== REGISTER DUMP(%s) ===========\n",
		host_name);

	for (offset = 0; offset < 0xA8; offset += 8) {
		PRT_DBG(" 0x%x: 0x%08x | 0x%x: 0x%08x\n",
			offset, READREG32((unsigned long)base_addr + offset),
			offset + 4,
			READREG32((unsigned long)base_addr + offset + 4));
	}

	PRT_DBG(" ===========================================\n");
}

/******************************************************************************
 * sau ldo
 *****************************************************************************/
#if !defined(CONFIG_ARCH_LOMBO_N7V5) && !defined(CONFIG_ARCH_LOMBO_N5V1)
/**
 * csp_sdc_set_ldo_volt - set sdc ldo
 * @volt:	0, use 3.3V voltage (SDC_VOLT_3_3);
 *		1, use 1.8V voltage (SDC_VOLT_1_8);
 */
void csp_sau_set_sdc_ldo(u32 volt)

{
#ifndef CONFIG_ARCH_LOMBO_N7V3
	reg_sau_sdc_ldo_t reg_sdc_ldo;

	PRT_TRACE_BEGIN("volt=%d\n", volt);

	reg_sdc_ldo.val = READREG32(VA_SAU_SDC_LDO);
	if (volt == SDC_VOLT_3_3) {
		reg_sdc_ldo.bits.en = 0;
	} else {
		reg_sdc_ldo.bits.en = 1;
		reg_sdc_ldo.bits.pd = SAU_SDC_LDO_PD_6MA;
		reg_sdc_ldo.bits.sel = SAU_SDC_LDO_VOL_1V80;
	}
	WRITEREG32(VA_SAU_SDC_LDO, reg_sdc_ldo.val);

	PRT_TRACE_END("\n");
#endif
}

/**
 * csp_sau_set_sdio_ldo - set sdc ldo
 * @volt:	0, use 3.3V voltage (SDC_VOLT_3_3);
 *		1, use 1.8V voltage (SDC_VOLT_1_8);
 */
void csp_sau_set_sdio_ldo(u32 volt)
{
#ifndef CONFIG_ARCH_LOMBO_N7V3
	reg_sau_sdio_ldo_t reg_sdio_ldo;

	PRT_TRACE_BEGIN("volt=%d\n", volt);

	reg_sdio_ldo.val = READREG32(VA_SAU_SDIO_LDO);
	if (volt == SDC_VOLT_3_3) {
		reg_sdio_ldo.bits.en = 0;
	} else {
		reg_sdio_ldo.bits.en = 1;
		reg_sdio_ldo.bits.sel = SAU_SDC_LDO_VOL_1V80;
	}
	WRITEREG32(VA_SAU_SDIO_LDO, reg_sdio_ldo.val);

	PRT_TRACE_END("\n");
#endif
}

/**
 * csp_sdc_set_ldo_volt - set sdc ldo
 * @enable:	0, use 3.3V voltage;
 *		1, use 1.8V voltage;
 */
void csp_sau_cfg_1v8_ldo(u32 sdc_idx, u32 enable)
{
	if (sdc_idx == 0) {
		if (enable == 0)
			csp_sau_set_sdc_ldo(SDC_VOLT_3_3);
		else
			csp_sau_set_sdc_ldo(SDC_VOLT_1_8);
	} else if (sdc_idx == 1) {
		if (enable == 0)
			csp_sau_set_sdio_ldo(SDC_VOLT_3_3);
		else
			csp_sau_set_sdio_ldo(SDC_VOLT_1_8);
	}
}
#else
void csp_sau_cfg_1v8_ldo(u32 sdc_idx, u32 enable)
{
}
#endif
#endif /* ___SDC___CSP__C___ */
